/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  <file> UlsLex.cpp </file>
  <brief>
 	The wrapper class of ulslex_t, the main structure of ULS c-library, libuls.*
    This file is part of ULS, Unified Lexical Scheme.
  </brief>
  <author>
    Stanley J. Hong <babel96@gmail.com>, 2011.
  </author>
*/

#include <iostream>
#include <string>

#include "UlsLex.h"
#include "uls.h"

using namespace std;
using namespace ULS;

#if defined(HAVE_LIBPTHREAD)
// <brief>
// Initializes the parameter 'mtx' and the 'mtx' can be used, locked/unlocked.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::initMutex(ULS::MutexType mtx)
{
	pthread_mutex_t *a_mtx = (pthread_mutex_t *) mtx;
	pthread_mutexattr_t attr;

	pthread_mutexattr_init(&attr);

	if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
		err_panic("error: fail to make the mutex recursive-one\n");

	if (pthread_mutex_init(a_mtx, &attr) != 0)
		err_panic("error: mutex init\n");

	pthread_mutexattr_destroy(&attr);
}

// <brief>
// De-initialize the 'mtx'.
// Hereafter , you must not use 'mtx'.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::deinitMutex(ULS::MutexType mtx)
{
	if (pthread_mutex_destroy(mtx) != 0)
		err_panic("error: mutex destroy\n");
}

// <brief>
// The lock API in ULS.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::lockMutex(ULS::MutexType mtx)
{
	int err;

	if ((err=pthread_mutex_lock(mtx)) != 0)
		err_panic("error to pthread_mutex_lock, err=%d", err);
}

// <brief>
// The unlock API in ULS.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::unlockMutex(ULS::MutexType mtx)
{
	if (pthread_mutex_unlock(mtx) != 0)
		err_panic("error to pthread_mutex_unlock");
}
#elif defined(WIN32)
// <brief>
// Initializes the parameter 'mtx' and the 'mtx' can be used, locked/unlocked.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::initMutex(ULS::MutexType mtx)
{
	mtx->hndl = CreateMutex(NULL, FALSE, NULL);

	if (mtx->hndl == INVALID_HANDLE_VALUE)
		err_panic("error: mutex init\n");
}
// <brief>
// De-initialize the 'mtx'.
// Hereafter , you must not use 'mtx'.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::deinitMutex(ULS::MutexType mtx)
{
	CloseHandle(mtx->hndl);
	mtx->hndl = INVALID_HANDLE_VALUE;
}

// <brief>
// The lock API in ULS.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::lockMutex(ULS::MutexType mtx)
{
	DWORD dwWaitResult;

	dwWaitResult = WaitForSingleObject(mtx->hndl, INFINITE);
	if (dwWaitResult != WAIT_OBJECT_0) {
		err_panic("error: mutex lock\n");
	}
}

// <brief>
// The unlock API in ULS.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::unlockMutex(ULS::MutexType mtx)
{
	ReleaseMutex(mtx->hndl);
}
#else

// <brief>
// Initializes the parameter 'mtx' and the 'mtx' can be used, locked/unlocked.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::initMutex(ULS::MutexType mtx)
{
}

// <brief>
// De-initialize the 'mtx'.
// Hereafter , you must not use 'mtx'.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::deinitMutex(ULS::MutexType mtx)
{
}

// <brief>
// The dummy lock API in ULS.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::lockMutex(ULS::MutexType mtx)
{
}

// <brief>
// The dummy unlock API in ULS.
// </brief>
// <parm name="mtx">A mutex object used in ULS</parm>
// <return>none</return>
void
ULS::unlockMutex(ULS::MutexType mtx)
{
}
#endif

// <brief>
// Lists the directories in which ULS-object in order searches for ulc-file(*.ulc)
// that would be specified in uls_create('confname', ...).
// </brief>
// <parm name="confname">The name of lexcial configuration, as like 'sample' or 'sample.ulc'</parm>
// <return>none</return>
void ULS::dumpSearchPathOfUlc(string& confname)
{
	 uls_list_ulc_searchpaths(confname.c_str());
}

// <brief>
// A constructor that creates a objec for lexical analysis.
// </brief>
// <parm name="ulc_file">The name/path of the lexical configuration.</parm>
// <return>none</return>
UlsLex::UlsLex(string& ulc_file)
{
	uls_lf_init(&uls_lf, uls_lf_puts_str);
	uls_lf_register_convspec(&uls_lf, "w", uls_fmtproc_coord);
	uls_lf_register_convspec(&uls_lf, "k", uls_fmtproc_tokname);

	if (uls_init(&uls, ulc_file.c_str()) < 0) {
#ifdef WIN32
		throw exception("fail to initialize uls");
#else
		err_panic("fail to initialize uls");
#endif
	}

	ULS::initMutex(&sysprn_g_mtx);
	sysprn = stdout;
	sysprn_puts = uls_lf_puts_file;
	uls_lf_csz_init(&mb_buf, NULL);
	sysprn_opened = 0;

	ULS::initMutex(&syserr_g_mtx);
	syslog = (void *) stderr;
	syslog_puts = uls_lf_puts_file;
	uls_lf_csz_init(&mb_lbuf, NULL);
}

// <brief>
// The destuctor of UlsLex.
// </brief>
// <return>none</return>
UlsLex::~UlsLex()
{
	uls_lf_csz_deinit(&mb_buf);
	ULS::deinitMutex(&sysprn_g_mtx);
	uls_deinit(&uls);

	uls_lf_deinit(&uls_lf);
}

// <brief>
// This sets the 'FileName' as the parameter 'fname'
// </brief>
// <parm name="fname">The new string value of 'FileName' to be updated</parm>
// <return>none</return>
void UlsLex::setFileName(string& fname)
{
	uls_istr_set_tag(&uls.i_str, fname.c_str(), -1);
	FileName = string(uls_get_tag(&uls));
}

// <brief>
// Sets the 'LineNum' forcibly or intially.
// </brief>
// <parm name="lineno">The new value of 'LineNum' to be updated</parm>
// <return>none</return>
void UlsLex::setLineNum(int lineno)
{
	uls_istr_set_tag(&uls.i_str, NULL, lineno);
	LineNum = uls_get_lineno(&uls);
}

// <brief>
// Adds some lines to the current line number to update the current LineNum forcibly.
// </brief>
// <parm name="amount">The amount of lines to be added</parm>
// <return>none</return>
void UlsLex::addLineNum(int amount)
{
	uls_istr_inc_lineno(&uls.i_str, amount);
	LineNum = uls_get_lineno(&uls);
}

// <brief>
// The log level of this object as it processes input files.
// </brief>
// <parm name="lvl">new value of log level</parm>
// <return>none</return>
void UlsLex::setLogLevel(int lvl)
{
	uls_set_loglevel(&uls, lvl);
}

// <brief>
// Clears the log levels defined in this class.
// </brief>
// <parm name="lvl">the log level to be cleared.</parm>
// <return>void</return>
void UlsLex::clearLogLevel(int lvl)
{
	uls_clear_loglevel(&uls, lvl);
}

// <brief>
// Checks if the 'lvl' is set in the internally.
// </brief>
// <parm name="lvl">the log level to be checked.</parm>
// <return>true/false</return>
bool UlsLex::isLogLevelSet(int lvl)
{
	return uls_loglevel_isset(&uls, lvl) ? true : false;
}

// <brief>
// Changes the keyword compare-interface internally defined.
// </brief>
// <parm name="proc">A new interface.</parm>
// <return>none</return>
void UlsLex::changeStrcmp(uls_strcmp_t proc)
{
	uls_change_strcmp(&uls, proc);
}

// <brief>
// Delete the literal string analyzing interface starting with 'pfx'
// </brief>
// <parm name="pfx">Finds the literal string analyzer.</parm>
// <return>none</return>
void UlsLex::deleteLiteralAnalyzer(string& pfx)
{
	istr_delete_lit_analyzer(&uls.i_str, (uls_char_t *) pfx.c_str());
}

// <brief>
// Changes the literal string analyzer to 'proc'.
// The 'proc' will be applied to the literal string starting with 'pfx'.
// </brief>
// <parm name="pfx">The prefix of literal string that will be processed by 'proc'</parm>
// <return>void</return>
void UlsLex::changeLiteralAnalyzer(string& pfx, istr_analyzer_t proc)
{
	 istr_change_lit_analyzer(&uls.i_str, (uls_char_t *) pfx.c_str(), proc);
}

// <brief>
// Sets the input file to be tokenized by giving its file descriptor 'fd'
// </brief>
// <parm name="fd">the file descriptor of input file.</parm>
// <parm name="fd_type">The type of 'fd'. The type of input file can be source(RAW) or ULS-file(*.uls).</parm>
// <parm name="tag">'tag' will be the field 'FileName' in this class.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>none</return>
void UlsLex::setFdType(int fd, int fd_type, string& tag, int flags)
{
	uls_set_fd_type(&uls, fd, fd_type, tag.c_str(), flags);
}

// <brief>
// This method will push the input-source specified by fd on the internal stack.
// The current input-source will be resumed at the end of 'fd' from the postion where this called.
// </brief>
// <parm name="fd">the file descriptor of input file.</parm>
// <parm name="fd_type">The type of 'fd'. The type of input file can be source(RAW) or ULS-file(*.uls).</parm>
// <parm name="tag">'tag' will be the field 'FileName' in this class.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return></return>
void UlsLex::pushFdType(int fd, int fd_type, string& tag, int flags)
{
	uls_push_fd_type(&uls, fd, fd_type, tag.c_str(), flags);
}

// <brief>
// This method does the same things as pushFdType() except this accepts the parameter hdr, the header info of uls-file.
// There are the attributes that pushFdType() needs to know.
// </brief>
// <parm name="fd">the file descriptor of input file.</parm>
// <parm name="hdr">the header information of uls-file(*.uls)</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>void</return>
void UlsLex::pushFdTypeWithHeader(int fd, uls_stream_t& hdr, int flags)
{
	uls_push_fd_type_hdr(&uls, &hdr, flags);
}

// <brief>
// This method pushes (raw) source-file on the internal stack.
// </brief>
// <parm name="fd">the file descriptor of input file.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>void</return>
void UlsLex::pushFd(int fd, int flags)
{
	 uls_push_fd_type(&uls, fd, ULS_STREAM_RAW, "?", flags);
}

// <brief>
// This overwrites or calls off the state of the current input-source and
// sets the new file of 'fd' to the input-source to be processed.
// </brief>
// <parm name="fd">the file descriptor of input file.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>none</return>
void UlsLex::setFd(int fd, int flags)
{
	uls_set_fd_type(&uls, fd, ULS_STREAM_RAW, "?", flags);
}

// <brief>
// This method will push the input-file with its header on the internal stack.
// The 'hdr' may be of RAW or of ULS(*.uls).
// </brief>
// <parm name="hdr">the header information of uls-file(*.uls)</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>void</return>
void UlsLex::pushFileWithHeader(uls_stream_t& hdr, int flags)
{
	uls_push_file_hdr(&uls, &hdr, flags);
}

// <brief>
// This method will push the input-file with its file path on the internal stack of input-sources.
// </brief>
// <parm name="filepath">The file path of input.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return></return>
void UlsLex::pushFile(string& filepath, int flags)
{
	uls_push_file(&uls, filepath.c_str(), flags);
}

// <brief>
// This method will overwrite the current input-source on the top of stack to new one with its header.
// </brief>
// <parm name="hdr">the header information of uls-file(*.uls)</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>none</return>
void UlsLex::setFileWithHeader(uls_stream_t& hdr, int flags)
{
	uls_set_file_hdr(&uls, &hdr, flags);
}

// <brief>
// This method will overwrite the current input-source on the top of stack to new one with its file path.
// </brief>
// <parm name="filepath">The file path of input.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>none</return>
void UlsLex::setFile(string& filepath, int flags)
{
	uls_set_file(&uls, filepath.c_str(), flags);
}

// <brief>
// This method will push the literal string as an input-source on the top of the stack.
// </brief>
// <parm name="line">A input source as an literal string</parm>
// <parm name="len">The length of 'line'</parm>
// <parm name="flags">You can set the flag ULS_WANT_EOF_TOK.</parm>
// <return>none</return>
void UlsLex::pushLine(char* line, int len, int flags)
{
	uls_push_line(&uls, line, len, flags);
}

// <brief>
// This method will overwrite the input-source on the top of the stack into 'line'.
// </brief>
// <parm name="line">A input source as an literal string</parm>
// <parm name="len">The length of 'line'</parm>
// <parm name="flags">You can set the flag ULS_WANT_EOF_TOK.</parm>
// <return>none</return>
void UlsLex::setLine(char* line, int len, int flags)
{
	uls_set_line(&uls, line, len, flags);
}

// <brief>
// Skips the white chars irregardless of token.
// </brief>
// <return>none</return>
void UlsLex::skipWhiteSpace(void)
{
	uls_skip_white_spaces(&uls);
}

// <brief>
// Peeks the next char in the internal buffer.
// Returns the corresponding information with the literal string in case that the next char is a quotation char.
// </brief>
// <parm name="pp_quotetype">out-param, The information of the literal string</parm>
// <return>The char which this object indicates to as the next char.</return>
char UlsLex::peekChInternal(istr_quotetype_t** pp_quotetype)
{
	return uls_peekch_detail(&uls, pp_quotetype);
}

// <brief>
// Peeks what is the next character in the internal buffer.
// </brief>
// <return>The next character</return>
char UlsLex::peekCh(void)
{
	return UlsLex::peekChInternal(NULL);
}

// <brief>
// Extracts the next character.
// </brief>
// <return>The next character</return>
char UlsLex::getCh(void)
{
	return uls_getch(&uls);
}

// <brief>
// Cancel the call of 'getCh()'
// </brief>
// <return>none</return>
void UlsLex::ungetCh(void)
{
	uls_ungetch(&uls);
}

// <brief>
// Advances the cursor of the lexical object to the next step.
// Returns an integer representing token id.
// </brief>
// <return></return>
int UlsLex::getToken(void)
{
	TokenId = uls_gettok(&uls);

	isEOI = (uls.tok == uls.toknum_EOI);
	isEOF = (uls.tok == uls.toknum_EOF);
	isIdentifer = (uls.tok == uls.toknum_ID);
	isNumber = (uls.tok == uls.toknum_NUMBER);
	isInteger = uls_is_integer(&uls) ? true : false;
	isReal = uls_is_real(&uls) ? true : false;

	if (isInteger) {
		LexemeInt = uls_lexeme_int(&uls);
	} else if (isReal) {
		LexemeDouble = uls_lexeme_double(&uls);
	}

	Lexeme = string((const char *) uls.s_val);
	LineNum = uls_get_lineno(&uls);
	FileName = string(uls_get_tag(&uls));

	return TokenId;
}

// <brief>
// Same as getToken().
// </brief>
// <return>The current token number.</return>
int UlsLex::next(void)
{
	return getToken();
}

// <brief>
// If the current token-id is not TokExpected, An Exception will be thrown.
// </brief>
// <parm name="TokExpected">I expect that the token be same as 'TokExpected'</parm>
// <return></return>
void UlsLex::expect(int TokExpected)
{
	uls_expect(&uls, TokExpected);
}

// <brief>
// An arbitrary token 'sym_no' will be the next token after this method is called.
// </brief>
// <parm name="sym_no">A token number</parm>
// <parm name="dat">The lexeme associated with 'sym_no'</parm>
// <parm name="dat_len">The length of 'dat'</parm>
// <return>none</return>
void UlsLex::pushback(int sym_no, void* dat, int dat_len)
{
	uls_pushback(&uls, sym_no, dat, dat_len);
}

// <brief>
// Call this method if you want get the current token again after getTok().
// </brief>
// <return>none</return>
void UlsLex::ungettok(void)
{
	uls_pushback(&uls, uls.tok, (void *) uls.s_val, uls.s_val_len);
}

// <brief>
// Prints the information on the current token.
// </brief>
// <return>none</return>
void UlsLex::printTok(void)
{
	uls_prntok(&uls);
}

// <brief>
// Return the string to which the token number is converted.
// </brief>
// <parm name="t">A token number</parm>
// <return>A string</return>
string UlsLex::getKeywordStr(int t)
{
	return string((const char *) uls_get_keyword(&uls, t));
}

// <brief>
// A method that emits a message after formatting the string 'fmt' with 'args'
// </brief>
// <parm name="fmt">The format string, a template for printing</parm>
// <parm name="args">The list of args</parm>
// <return>none</return>
void UlsLex::vlog(const char* fmt, va_list args)
{
	char numbuf[N_CHARS_LOGBUF];
	int len;

	ULS::lockMutex(&syserr_g_mtx);

	len = uls_snprintf(numbuf, N_CHARS_LOGBUF, "[%s:%d] ",
		uls_get_tag(&uls), uls_get_lineno(&uls));
	uls.syslog_puts(uls.syslog, numbuf, len);

	uls_lf_vxprintf_generic(&uls_lf, uls.syslog, uls.syslog_puts, &uls, fmt, args);
	uls.syslog_puts(uls.syslog, "\n", 1);

	ULS::unlockMutex(&syserr_g_mtx);
}

// <brief>
// Logs a formatted message if the 'loglvl' is set in the object.
// </brief>
// <parm name="loglvl">This message will be printed if 'loglvl' is set.</parm>
// <return>none</return>
void UlsLex::log(int loglvl, const char* fmt, ...)
{
	va_list	args;

	if (!(uls.loglevel & ULS_LOGLEVEL_FLAG(loglvl)))
		return;

	va_start(args, fmt);
	vlog(fmt, args);
	va_end(args);
}

// <brief>
// Logs a formatted message
// You can use %k %w to print the current token, its location.
// No need to append '\n' to the end of line 'fmt'
// </brief>
// <parm name="fmt">The template for message string</parm>
// <return>void</return>
void UlsLex::log(const char* fmt, ...)
{
	va_list	args;

	va_start(args, fmt);
	vlog(fmt, args);
	va_end(args);
}

// <brief>
// Logs a formatted message and the program will die.
// No need to append '\n' to the end of line 'fmt'
// </brief>
// <parm name="fmt">The template for message string</parm>
// <return>none</return>
void UlsLex::panic(const char* fmt, ...)
{
	va_list	args;

	va_start(args, fmt);
	vlog(fmt, args);
	va_end(args);

	exit(1);
}

// <brief>
// A method that emits a wide string message after formatting the string 'wfmt' with 'args'
// </brief>
// <parm name="wfmt">The format string, a template for printing</parm>
// <parm name="args">The list of args</parm>
// <return>none</return>
void UlsLex::vwlog(const wchar_t* wfmt, va_list args)
{
	int len;

	ULS::lockMutex(&syserr_g_mtx);

	uls_lf_csz_reset(&uls.mb_lbuf, NULL);
	len = uls_lf_zwprintf(&uls.mb_lbuf, &uls, L"[%s:%d] ",
		uls_get_tag(&uls), uls_get_lineno(&uls));
	uls.syslog_puts(uls.syslog, (uls_lf_char_t *) csz_text(&uls.mb_lbuf.csz), len);

	uls_lf_csz_reset(&uls.mb_lbuf, NULL);
	len = uls_lf_vzwprintf(&uls_lf, &uls.mb_lbuf, &uls, wfmt, args);

	uls.syslog_puts(uls.syslog, (uls_lf_char_t *) csz_text(&uls.mb_lbuf.csz), len);
	uls.syslog_puts(uls.syslog, "\n", 1);

	ULS::unlockMutex(&syserr_g_mtx);
}

// <brief>
// An wide string version of the method 'log()'
// </brief>
// <parm name="loglvl">This message will be printed if 'loglvl' is set.</parm>
// <parm name="wfmt">The template for message string</parm>
// <return>none</return>
void UlsLex::wlog(int loglvl, const wchar_t* wfmt, ...)
{
	va_list	args;

	if (!(uls.loglevel & ULS_LOGLEVEL_FLAG(loglvl)))
		return;

	va_start(args, wfmt);
	vwlog(wfmt, args);
	va_end(args);
}

// <brief>
// An wide string version of the method 'log()'
// </brief>
// <parm name="loglvl">This message will be printed if 'loglvl' is set.</parm>
// <parm name="wfmt">The template for message string</parm>
// <return>none</return>
void UlsLex::wlog(const wchar_t* wfmt, ...)
{
	va_list	args;

	va_start(args, wfmt);
	vwlog(wfmt, args);
	va_end(args);
}

// <brief>
// An wide string version of the method 'panic()'
// </brief>
// <parm name="wfmt">The template for message string</parm>
// <return>none</return>
void UlsLex::wpanic(const wchar_t* wfmt, ...)
{
	va_list	args;

	va_start(args, wfmt);
	vwlog(wfmt, args);
	va_end(args);

	exit(1);
}

// <brief>
// Prints the wide string formatted by variable arguments in the varargs(stdarg) style.
// </brief>
// <parm name="wfmt">The format string, a template for printing</parm>
// <parm name="args">The list of args</parm>
// <parm name="p_wlen">a out-parameter, the length of returning string, # of wchar_t's</parm>
// <return>The formatted wide string</return>
uls_lf_word_t*
UlsLex::vwprint(const uls_lf_word_t* wfmt, va_list args, int* p_wlen)
{
	uls_lf_word_t *wptr;
	char *ptr;
	int len, len2;
	size_t siz;

	uls_lf_csz_reset(&mb_buf, NULL);
	len = uls_lf_vzwprintf(&uls_lf, &mb_buf, NULL, wfmt, args);

	if ((siz=mbstowcs(NULL, (char *) csz_text(&mb_buf.csz), 0)) == (size_t) -1) {
		return NULL;
	}

	csz_add_eos(&mb_buf.csz);
	len2 = uls_roundup(csz_length(&mb_buf.csz), sizeof(uls_lf_word_t));

	csz_append(&mb_buf.csz, NULL, (len2-len) + (siz + 1) *sizeof(uls_lf_word_t));

	ptr = (char *) csz_text(&mb_buf.csz);
	wptr = (uls_lf_word_t *) (ptr + len2);

	if ((siz = mbstowcs(wptr, ptr, siz + 1)) == (size_t) -1) {
		*p_wlen = -1;
		wptr = NULL;
	} else {
		*p_wlen = (int) siz;
	}

	return wptr;
}

// <brief>
// Opens a file for output.
// After calls of print() or wprint(), be sure to call closeOutput().
// </brief>
// <parm name="out_file">The output file path</parm>
// <return>none</return>
void UlsLex::openOutput(string& out_file)
{
	FILE *fp;

	if ((fp = fopen(out_file.c_str(), "w")) == NULL) {
#ifdef WIN32
		throw exception("fail to create an output file.");
#else
		err_panic("fail to create file");
#endif
	}

	ULS::lockMutex(&sysprn_g_mtx);

	if (sysprn_opened) {
		ULS::unlockMutex(&sysprn_g_mtx);
	} else {
		sysprn_opened = 1;
		sysprn = (void *) fp;
		sysprn_puts = uls_lf_puts_file;
	}
}

// <brief>
// closes the output file used by print() or wprint().
// </brief>
// <return>none</return>
void UlsLex::closeOutput(void)
{
	fclose((FILE *) sysprn);
	sysprn = (void *) stdout;
	sysprn_puts = uls_lf_puts_file;
	sysprn_opened = 0;
	ULS::unlockMutex(&sysprn_g_mtx);
}

// <brief>
// This will print the formatted string to the standard oupput, by default.
// But you can change the output port by calling 
// </brief>
// <parm name="fmt">The template for message string</parm>
// <return># of chars printed</return>
int UlsLex::print(const uls_lf_char_t* fmt, ...)
{
	va_list	args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vxprintf_generic(&uls_lf, sysprn, sysprn_puts, &uls, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// An wide string version of the method 'print()'
// </brief>
// <parm name="wfmt">The template for message string</parm>
// <return># of wchar_t's</return>
int UlsLex::wprint(const uls_lf_word_t* wfmt, ...)
{
	va_list	args;
	uls_lf_word_t *wptr;
	int wlen;

	va_start(args, wfmt);
	wptr = vwprint(wfmt, args, &wlen);
	sysprn_puts(sysprn, (uls_lf_char_t *) wptr, wlen*sizeof(uls_lf_word_t));
	va_end(args);

	return wlen;
}

// <brief>
// This stores the formatted string to the 'buf'.
// </brief>
// <parm name="buf">The output buffer for the formatted string</parm>
// <parm name="bufsiz">The size of 'buf'</parm>
// <parm name="fmt">The template for message string</parm>
// <return># of chars filled except for '\0'</return>
int
UlsLex::snprintf(uls_lf_char_t* buf, int bufsiz, const uls_lf_char_t *fmt, ...)
{
	buf4legacy_t stdbuf;
	va_list args;
	int len;

	if (bufsiz <= 1) {
		if (bufsiz==1) {
			buf[0] = '\0';
		}
		return 0;
	}

	stdbuf.flags = 0;
	stdbuf.bufptr = stdbuf.buf = buf;
	stdbuf.buflen = bufsiz - 1;

	va_start(args, fmt);
	len = uls_lf_vxprintf_generic(&uls_lf, &stdbuf, uls_lf_puts_str, &uls, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// Stores a wide string formatted by the string 'wfmt' with 'args' to 'wbuf'.
// </brief>
// <parm name="wbuf">The buffer of wchar_t to store the result.</parm>
// <parm name="wbuf_siz">The size of 'wbuf', # of wchar_t's</parm>
// <parm name="wfmt">The format string, a template for printing</parm>
// <parm name="args">The list of args</parm>
// <return># of wchar_t's int 'wbuf'</return>
int
UlsLex::snvwprintf(uls_lf_word_t* wbuf, int wbuf_siz, const uls_lf_word_t *wfmt, va_list args)
{
	size_t wlen;
	int len;

	if (wbuf_siz <= 1) {
		if (wbuf_siz==1) wbuf[0] = L'\0';
		return wbuf_siz;
	}

	uls_lf_csz_reset(&mb_buf, NULL);
	len = uls_lf_vzwprintf(&uls_lf, &mb_buf, NULL, wfmt, args);

	if ((wlen=mbstowcs(wbuf, (char *) csz_text(&mb_buf.csz), wbuf_siz)) == (size_t) -1) {
		len = -1;
	} else {
		len = (int) wlen;
	}

	return len;
}

// <brief>
// An wide string version of the method 'snprintf()'
// </brief>
// <parm name="wbuf">The output buffer for the formatted string</parm>
// <parm name="wbufsiz">The size of 'buf', # of wchar_t's</parm>
// <parm name="wfmt">The template for message string</parm>
// <return># of wchar_t's</return>
int
UlsLex::snwprintf(uls_lf_word_t* wbuf, int wbuf_siz, const uls_lf_word_t *wfmt, ...)
{
	va_list args;
	int wlen;

	va_start(args, wfmt);
	wlen = snvwprintf(wbuf, wbuf_siz, wfmt, args);
	va_end(args);

	return wlen;
}

// <brief>
// Stores a 'char' string formatted by the string 'fmt' with 'args' to 'buf'.
// </brief>
// <parm name="buf">The buffer of char to store the result.</parm>
// <parm name="fmt">The format string, a template for printing</parm>
// <parm name="args">The list of args</parm>
// <return># of chars in 'buf'</return>
int
UlsLex::vsprintf(uls_lf_char_t *buf, const uls_lf_char_t *fmt, va_list args)
{
	buf4legacy_t stdbuf;

	stdbuf.flags = 0;
	stdbuf.bufptr = stdbuf.buf = buf;
	stdbuf.buflen = (~0U) >> 1;

	return uls_lf_vxprintf_generic(&uls_lf, &stdbuf, uls_lf_puts_str, &uls, fmt, args);
}

// <brief>
// This stores the formatted string to the 'buf' with its size unknown.
// I recommend not to use this but snprintf().
// </brief>
// <parm name="buf">The output buffer for the formatted string</parm>
// <parm name="fmt">The template for message string</parm>
// <return># of chars filled</return>
int
UlsLex::sprintf(uls_lf_char_t * buf, const uls_lf_char_t *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = vsprintf(buf, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// An wide string version of the method 'sprintf()'
// </brief>
// <parm name="wbuf">The output buffer for the formatted string</parm>
// <parm name="wfmt">The template for message string</parm>
// <return></return>
int
UlsLex::swprintf(uls_lf_word_t * wbuf, const uls_lf_word_t *wfmt, ...)
{
	va_list args;
	int wlen;

	va_start(args, wfmt);
	wlen = snvwprintf(wbuf, (~0U) >> 1, wfmt, args);
	va_end(args);

	return wlen;
}

// <brief>
// This stores the formatted string to the 'fp', FILE*.
// </brief>
// <parm name="fp">The pointer of FILE to be wrriten</parm>
// <parm name="fmt">The template for message string</parm>
// <return># of chars written</return>
int
UlsLex::fprintf(FILE* fp, const uls_lf_char_t *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vxprintf_generic(&uls_lf, fp, uls_lf_puts_file, &uls, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// An wide string version of the method 'fprintf()'
// </brief>
// <parm name="fp">The pointer of FILE to be wrriten</parm>
// <parm name="wfmt">The template for message string</parm>
// <return># of wchar_t's written</return>
int
UlsLex::fwprintf(FILE* fp, const uls_lf_word_t *wfmt, ...)
{
	va_list args;
	uls_lf_word_t *wptr;
	int wlen;

	va_start(args, wfmt);
	wptr = vwprint(wfmt, args, &wlen);
	uls_lf_puts_file(fp, (uls_lf_char_t *) wptr, wlen*sizeof(uls_lf_word_t));
	va_end(args);

	return wlen;
}

// <brief>
// This prints the formatted string to the 'stdout'.
// </brief>
// <parm name="fmt">The template for message string</parm>
// <return># of chars printed</return>
int
UlsLex::printf(const uls_lf_char_t *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vxprintf_generic(&uls_lf, stdout, uls_lf_puts_file, &uls, fmt, args);
	va_end(args);

	return len;
}

// <brief>
// An wide string version of the method 'printf()'
// </brief>
// <parm name="wfmt">The template for message string</parm>
// <return># of chars printed</return>
int
UlsLex::wprintf(const uls_lf_word_t *wfmt, ...)
{
	va_list args;
	int wlen;

	va_start(args, wfmt);
	wlen = uls_lf_vsnwprintf_generic(&uls_lf, stdout, uls_lf_puts_file, NULL, wfmt, args);
	va_end(args);

	return wlen;
}

// <brief>
// Changes the associated procedure with 'percent_name', a converion specification.
// The procedure will output string with puts_proc which can be set by 'changePuts'
// </brief>
// <parm name="percent_name">A converion specification without the percent character itself.</parm>
// <parm name="proc">The user defined procedure for processing '%percent_name'</parm>
// <return>none</return>
void
UlsLex::changeConvSpec(uls_lf_char_t* percent_name, uls_lf_convspec_t proc)
{
	uls_lf_register_convspec(&uls_lf, percent_name, proc);
}

// <brief>
// You can use this method to change the default output interface for logging.
// </brief>
// <parm name="puts_proc">A newly output interface</parm>
// <return>none</return>
void
UlsLex::changePuts(uls_lf_puts_t puts_proc)
{
	uls_lf_change_puts(&uls_lf, puts_proc);
}
